/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * This code is derived from BasicDataSourceFactory (DBCP 3.0 RC1) * * Contributors: * Florent Guillaume, Nuxeo */ package org.nuxeo.runtime.datasource; import java.io.ByteArrayInputStream; import java.sql.Connection; import java.sql.SQLException; import java.util.Collections; import java.util.Enumeration; import java.util.Hashtable; import java.util.Properties; import java.util.StringTokenizer; import javax.naming.Context; import javax.naming.Name; import javax.naming.RefAddr; import javax.naming.Reference; import javax.naming.spi.ObjectFactory; import javax.sql.DataSource; import javax.sql.XADataSource; import org.apache.commons.dbcp.managed.BasicManagedDataSource; /** * JNDI object factory for DBCP that creates an instance of * <code>BasicManagedDataSource</code> that has been configured based on the * <code>RefAddr</code> values of the specified <code>Reference</code>, which * must match the names and data types of the * <code>BasicManagedDataSource</code> bean properties. * * @author Craig R. McClanahan * @author Dirk Verbeeck * @author Florent Guillaume */ public class BasicManagedDataSourceFactory implements ObjectFactory { private static final String PROP_DEFAULTAUTOCOMMIT = "defaultAutoCommit"; private static final String PROP_DEFAULTREADONLY = "defaultReadOnly"; private static final String PROP_DEFAULTTRANSACTIONISOLATION = "defaultTransactionIsolation"; private static final String PROP_DEFAULTCATALOG = "defaultCatalog"; private static final String PROP_DRIVERCLASSNAME = "driverClassName"; private static final String PROP_MAXACTIVE = "maxActive"; private static final String PROP_MAXIDLE = "maxIdle"; private static final String PROP_MINIDLE = "minIdle"; private static final String PROP_INITIALSIZE = "initialSize"; private static final String PROP_MAXWAIT = "maxWait"; private static final String PROP_TESTONBORROW = "testOnBorrow"; private static final String PROP_TESTONRETURN = "testOnReturn"; private static final String PROP_TIMEBETWEENEVICTIONRUNSMILLIS = "timeBetweenEvictionRunsMillis"; private static final String PROP_NUMTESTSPEREVICTIONRUN = "numTestsPerEvictionRun"; private static final String PROP_MINEVICTABLEIDLETIMEMILLIS = "minEvictableIdleTimeMillis"; private static final String PROP_TESTWHILEIDLE = "testWhileIdle"; private static final String PROP_PASSWORD = "password"; private static final String PROP_URL = "url"; private static final String PROP_USERNAME = "username"; private static final String PROP_VALIDATIONQUERY = "validationQuery"; private static final String PROP_VALIDATIONQUERY_TIMEOUT = "validationQueryTimeout"; private static final String PROP_INITCONNECTIONSQLS = "initConnectionSqls"; private static final String PROP_ACCESSTOUNDERLYINGCONNECTIONALLOWED = "accessToUnderlyingConnectionAllowed"; private static final String PROP_REMOVEABANDONED = "removeAbandoned"; private static final String PROP_REMOVEABANDONEDTIMEOUT = "removeAbandonedTimeout"; private static final String PROP_LOGABANDONED = "logAbandoned"; private static final String PROP_POOLPREPAREDSTATEMENTS = "poolPreparedStatements"; private static final String PROP_MAXOPENPREPAREDSTATEMENTS = "maxOpenPreparedStatements"; protected static final String PROP_CONNECTIONPROPERTIES = "connectionProperties"; // Managed: public static final String PROP_XADATASOURCE = "xaDataSource"; private static final String[] ALL_PROPERTIES = { // PROP_DEFAULTAUTOCOMMIT, // PROP_DEFAULTREADONLY, // PROP_DEFAULTTRANSACTIONISOLATION, // PROP_DEFAULTCATALOG, // PROP_DRIVERCLASSNAME, // PROP_MAXACTIVE, // PROP_MAXIDLE, // PROP_MINIDLE, // PROP_INITIALSIZE, // PROP_MAXWAIT, // PROP_TESTONBORROW, // PROP_TESTONRETURN, // PROP_TIMEBETWEENEVICTIONRUNSMILLIS, // PROP_NUMTESTSPEREVICTIONRUN, // PROP_MINEVICTABLEIDLETIMEMILLIS, // PROP_TESTWHILEIDLE, // PROP_PASSWORD, // PROP_URL, // PROP_USERNAME, // PROP_VALIDATIONQUERY, // PROP_VALIDATIONQUERY_TIMEOUT, // PROP_INITCONNECTIONSQLS, // PROP_ACCESSTOUNDERLYINGCONNECTIONALLOWED, // PROP_REMOVEABANDONED, // PROP_REMOVEABANDONEDTIMEOUT, // PROP_LOGABANDONED, // PROP_POOLPREPAREDSTATEMENTS, // PROP_MAXOPENPREPAREDSTATEMENTS, // PROP_CONNECTIONPROPERTIES, // PROP_XADATASOURCE // }; // copied from PoolableConnectionFactory private static final int UNKNOWN_TRANSACTIONISOLATION = -1; // -------------------------------------------------- ObjectFactory Methods /** * Create and return a new <code>BasicManagedDataSource</code> instance. If * no instance can be created, return <code>null</code> instead. * * @param obj The possibly null object containing location or reference * information that can be used in creating an object * @param name The name of this object relative to <code>nameCtx</code> * @param nameCtx The context relative to which the <code>name</code> * parameter is specified, or <code>null</code> if * <code>name</code> is relative to the default initial context * @param environment The possibly null environment that is used in creating * this object * * @exception Exception if an exception occurs creating the instance */ @Override public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws Exception { // We only know how to deal with <code>javax.naming.Reference</code>s // that specify a class name of "javax.sql.DataSource" if ((obj == null) || !(obj instanceof Reference)) { return null; } Reference ref = (Reference) obj; if (!"javax.sql.DataSource".equals(ref.getClassName())) { return null; } Properties properties = new Properties(); for (String propertyName : ALL_PROPERTIES) { RefAddr ra = ref.get(propertyName); if (ra != null) { String propertyValue = ra.getContent().toString(); properties.setProperty(propertyName, propertyValue); } } return createDataSource(properties); } /** * Creates and configures a {@link BasicManagedDataSource} instance based on * the given properties. * * @param properties the datasource configuration properties * @throws Exception if an error occurs creating the data source */ public static DataSource createDataSource(Properties properties) throws Exception { BasicManagedDataSource dataSource = new BasicManagedDataSource(); String value = properties.getProperty(PROP_DEFAULTAUTOCOMMIT); if (value != null) { dataSource.setDefaultAutoCommit(Boolean.valueOf(value).booleanValue()); } value = properties.getProperty(PROP_DEFAULTREADONLY); if (value != null) { dataSource.setDefaultReadOnly(Boolean.valueOf(value).booleanValue()); } value = properties.getProperty(PROP_DEFAULTTRANSACTIONISOLATION); if (value != null) { int level = UNKNOWN_TRANSACTIONISOLATION; if ("NONE".equalsIgnoreCase(value)) { level = Connection.TRANSACTION_NONE; } else if ("READ_COMMITTED".equalsIgnoreCase(value)) { level = Connection.TRANSACTION_READ_COMMITTED; } else if ("READ_UNCOMMITTED".equalsIgnoreCase(value)) { level = Connection.TRANSACTION_READ_UNCOMMITTED; } else if ("REPEATABLE_READ".equalsIgnoreCase(value)) { level = Connection.TRANSACTION_REPEATABLE_READ; } else if ("SERIALIZABLE".equalsIgnoreCase(value)) { level = Connection.TRANSACTION_SERIALIZABLE; } else { try { level = Integer.parseInt(value); } catch (NumberFormatException e) { System.err.println("Could not parse defaultTransactionIsolation: " + value); System.err.println("WARNING: defaultTransactionIsolation not set"); System.err.println("using default value of database driver"); level = UNKNOWN_TRANSACTIONISOLATION; } } dataSource.setDefaultTransactionIsolation(level); } value = properties.getProperty(PROP_DEFAULTCATALOG); if (value != null) { dataSource.setDefaultCatalog(value); } value = properties.getProperty(PROP_DRIVERCLASSNAME); if (value != null) { dataSource.setDriverClassName(value); } value = properties.getProperty(PROP_MAXACTIVE); if (value != null) { dataSource.setMaxActive(Integer.parseInt(value)); } value = properties.getProperty(PROP_MAXIDLE); if (value != null) { dataSource.setMaxIdle(Integer.parseInt(value)); } value = properties.getProperty(PROP_MINIDLE); if (value != null) { dataSource.setMinIdle(Integer.parseInt(value)); } value = properties.getProperty(PROP_INITIALSIZE); if (value != null) { dataSource.setInitialSize(Integer.parseInt(value)); } value = properties.getProperty(PROP_MAXWAIT); if (value != null) { dataSource.setMaxWait(Long.parseLong(value)); } value = properties.getProperty(PROP_TESTONBORROW); if (value != null) { dataSource.setTestOnBorrow(Boolean.valueOf(value).booleanValue()); } value = properties.getProperty(PROP_TESTONRETURN); if (value != null) { dataSource.setTestOnReturn(Boolean.valueOf(value).booleanValue()); } value = properties.getProperty(PROP_TIMEBETWEENEVICTIONRUNSMILLIS); if (value != null) { dataSource.setTimeBetweenEvictionRunsMillis(Long.parseLong(value)); } value = properties.getProperty(PROP_NUMTESTSPEREVICTIONRUN); if (value != null) { dataSource.setNumTestsPerEvictionRun(Integer.parseInt(value)); } value = properties.getProperty(PROP_MINEVICTABLEIDLETIMEMILLIS); if (value != null) { dataSource.setMinEvictableIdleTimeMillis(Long.parseLong(value)); } value = properties.getProperty(PROP_TESTWHILEIDLE); if (value != null) { dataSource.setTestWhileIdle(Boolean.valueOf(value).booleanValue()); } value = properties.getProperty(PROP_PASSWORD); if (value != null) { dataSource.setPassword(value); } value = properties.getProperty(PROP_URL); if (value != null) { dataSource.setUrl(value); } value = properties.getProperty(PROP_USERNAME); if (value != null) { dataSource.setUsername(value); } value = properties.getProperty(PROP_VALIDATIONQUERY); if (value != null) { dataSource.setValidationQuery(value); } value = properties.getProperty(PROP_VALIDATIONQUERY_TIMEOUT); if (value != null) { dataSource.setValidationQueryTimeout(Integer.parseInt(value)); } value = properties.getProperty(PROP_ACCESSTOUNDERLYINGCONNECTIONALLOWED); if (value != null) { dataSource.setAccessToUnderlyingConnectionAllowed(Boolean.valueOf( value).booleanValue()); } value = properties.getProperty(PROP_REMOVEABANDONED); if (value != null) { dataSource.setRemoveAbandoned(Boolean.valueOf(value).booleanValue()); } value = properties.getProperty(PROP_REMOVEABANDONEDTIMEOUT); if (value != null) { dataSource.setRemoveAbandonedTimeout(Integer.parseInt(value)); } value = properties.getProperty(PROP_LOGABANDONED); if (value != null) { dataSource.setLogAbandoned(Boolean.valueOf(value).booleanValue()); } value = properties.getProperty(PROP_POOLPREPAREDSTATEMENTS); if (value != null) { dataSource.setPoolPreparedStatements(Boolean.valueOf(value).booleanValue()); } value = properties.getProperty(PROP_MAXOPENPREPAREDSTATEMENTS); if (value != null) { dataSource.setMaxOpenPreparedStatements(Integer.parseInt(value)); } value = properties.getProperty(PROP_INITCONNECTIONSQLS); if (value != null) { StringTokenizer tokenizer = new StringTokenizer(value, ";"); dataSource.setConnectionInitSqls(Collections.list(tokenizer)); } value = properties.getProperty(PROP_CONNECTIONPROPERTIES); if (value != null) { Properties p = getProperties(value); Enumeration<?> e = p.propertyNames(); while (e.hasMoreElements()) { String propertyName = (String) e.nextElement(); dataSource.addConnectionProperty(propertyName, p.getProperty(propertyName)); } } // Managed: initialize XADataSource value = properties.getProperty(PROP_XADATASOURCE); if (value != null) { Class<?> xaDataSourceClass; try { xaDataSourceClass = Class.forName(value); } catch (Throwable t) { throw (SQLException) new SQLException( "Cannot load XA data source class '" + value + "'").initCause(t); } XADataSource xaDataSource; try { xaDataSource = (XADataSource) xaDataSourceClass.newInstance(); } catch (Throwable t) { throw (SQLException) new SQLException( "Cannot create XA data source of class '" + value + "'").initCause(t); } dataSource.setXaDataSourceInstance(xaDataSource); } // DBCP-215 // Trick to make sure that initialSize connections are created if (dataSource.getInitialSize() > 0) { dataSource.getLogWriter(); } // Return the configured DataSource instance return dataSource; } /** * Parse properties from the string. Format of the string must be * [propertyName=property;]* * * @param propText * @return Properties * @throws Exception */ private static Properties getProperties(String propText) throws Exception { Properties p = new Properties(); if (propText != null) { p.load(new ByteArrayInputStream( propText.replace(';', '\n').getBytes())); } return p; } }